# PostgreSQLParameterBinders.java

    package org.codefilarete.stalactite.sql.statement.binder;

    import java.io.IOException;
    import java.io.InputStream;
    import java.io.OutputStream;
    import java.sql.Blob;
    import java.sql.SQLException;

    import org.codefilarete.stalactite.sql.statement.binder.DefaultParameterBinders;
    import org.codefilarete.stalactite.sql.statement.binder.DefaultResultSetReaders;
    import org.codefilarete.stalactite.sql.statement.binder.NullAwareParameterBinder;
    import org.codefilarete.stalactite.sql.statement.binder.NullAwarePreparedStatementWriter;
    import org.codefilarete.stalactite.sql.statement.binder.NullAwareResultSetReader;
    import org.codefilarete.stalactite.sql.statement.binder.ParameterBinder;
    import org.codefilarete.tool.io.IOs;
    import org.postgresql.PGConnection;
    import org.postgresql.largeobject.LargeObject;
    import org.postgresql.largeobject.LargeObjectManager;

    /**
     * @author Guillaume Mary
     */
    public final class PostgreSQLParameterBinders {

    	/**
    	 * Equivalent to {@link DefaultParameterBinders#BLOB_BINDER} but uses PostgreSQL {@link LargeObjectManager} to write {@link Blob}
    	 * as described in PostGreSQL documentation
    	 */
    	public static final ParameterBinder<Blob> BLOB_BINDER = new NullAwareParameterBinder<>(new NullAwareResultSetReader<>(DefaultResultSetReaders.BLOB_READER),
    																						   new NullAwarePreparedStatementWriter<>((preparedStatement, valueIndex, value) -> {
    				// Note that column is expected to be of type OID
    				// see https://jdbc.postgresql.org/documentation/head/binary-data.html#binary-data-example
    				PGConnection connection = preparedStatement.getConnection().unwrap(PGConnection.class);
    				LargeObjectManager lobj = connection.getLargeObjectAPI();
    				long oid = lobj.createLO(LargeObjectManager.READ | LargeObjectManager.WRITE);
    				try (LargeObject obj = lobj.open(oid, LargeObjectManager.WRITE)) {
    					try (OutputStream outputStream = obj.getOutputStream();
    						InputStream binaryStream = value.getBinaryStream()) {
    						IOs.copy(binaryStream, outputStream, 2048);
    					} catch (IOException e) {
    						throw new SQLException("Blob can't be copied as a PostgreSQL LargeObject", e);
    					}
    				}
    				preparedStatement.setLong(valueIndex, oid);
    			}));

    	private PostgreSQLParameterBinders() {
    	}
    }